import sys
sys.path.append("..")
import pyrtl
from  pyrtl import GPUSim
import or1200_definitions

dw = 48
fw = or1200_definitions.OR1200_SB_LOG
fl = or1200_definitions.OR1200_SB_ENTRIES

class Or1200_sb_fifo(object):
    def __init__(self):
        # self.clk_i = pyrtl.Input(bitwidth=1, name='clk_i')
        self.rst_i = pyrtl.Input(bitwidth=1, name='rst_i')
        self.dat_i = pyrtl.Input(bitwidth=dw, name='dat_i')
        self.wr_i = pyrtl.Input(bitwidth=1, name='wr_i')
        self.rd_i = pyrtl.Input(bitwidth=1, name='rd_i')

        self.dat_o = pyrtl.Output(bitwidth=dw, name='dat_o')
        #self.full_o = pyrtl.Output(bitwidth=1, name='full_o')
        #self.empty_o = pyrtl.Output(bitwidth=1, name='empty_o')

        self.mem = pyrtl.Register(bitwidth=dw*fl, name='mem')
        #self.dat_o = pyrtl.Register(bitwidth=dw, name='dat_o')
        self.cntr = pyrtl.Register(bitwidth=fw+2, name='cntr')
        self.wr_pntr = pyrtl.Register(bitwidth=fw, name='wr_pntr')
        self.rd_pntr = pyrtl.Register(bitwidth=fw, name='rd_pntr')
        self.empty_o = pyrtl.Register(bitwidth=1, name='empty_o')
        self.full_o = pyrtl.Register(bitwidth=1, name='full_o')

        # initialize all the parts
        fifo = Fifo()

        # establish connection relations for fifo
        # fifo.clk_i <<= self.clk_i
        fifo.rst_i <<= self.rst_i
        fifo.wr_i <<= self.wr_i
        fifo.rd_i <<= self.rd_i
        fifo.dat_i <<= self.dat_i
        self.dat_o <<= fifo.dat_o
        self.cntr.next <<= fifo.cntr
        self.wr_pntr.next <<= fifo.wr_pntr
        self.rd_pntr.next <<= fifo.rd_pntr
        self.empty_o.next <<= fifo.empty_o
        self.full_o.next <<= fifo.full_o
        self.mem.next <<= fifo.mem


class Fifo(object):
    def __init__(self):
        # self.clk_i = pyrtl.WireVector(bitwidth=1, name='clk_i')
        self.rst_i = pyrtl.WireVector(bitwidth=1)
        self.wr_i = pyrtl.WireVector(bitwidth=1)
        self.rd_i = pyrtl.WireVector(bitwidth=1)
        self.dat_i = pyrtl.WireVector(bitwidth=dw)

        self.dat_o = pyrtl.WireVector(bitwidth=dw)
        self.cntr = pyrtl.Register(bitwidth=fw + 2)
        self.wr_pntr = pyrtl.Register(bitwidth=fw)
        self.rd_pntr = pyrtl.Register(bitwidth=fw)
        self.empty_o = pyrtl.Register(bitwidth=1)
        self.full_o = pyrtl.Register(bitwidth=1)
        self.mem = pyrtl.Register(bitwidth=dw*fl)

        with pyrtl.conditional_assignment:
            with self.rst_i:
                self.full_o.next |= pyrtl.Const(0b0, bitwidth=1)     # full_o <= #1 1'b0
                self.empty_o.next |= pyrtl.Const(0b1, bitwidth=1)    # empty_o <= #1 1'b1
                self.wr_pntr.next |= pyrtl.Const(0b0 * fw, bitwidth=fw)  # wr_pntr <= #1 {fw{1'b0}}
                self.rd_pntr.next |= pyrtl.Const(0b0 * fw, bitwidth=fw)  # rd_pntr <= #1 {fw{1'b0}}
                self.cntr.next |= pyrtl.Const(fw + 2 * 0b0, bitwidth=fw + 2) # cntr <= #1 {fw+2{1'b0}}
                self.dat_o |= pyrtl.Const(0b0 * dw, bitwidth=dw)    # dat_o <= #1 {dw{1'b0}}
            # with self.wr_i:
            #     with self.rd_i:
            #         self.mem |= self.dat_i  # mem[wr_pntr] <= #1 dat_i
            with self.wr_i.__and__(self.rd_i): # FIFO Read and Write
                self.mem.next |= self.dat_i    # mem[wr_pntr] <= #1 dat_i
                with self.wr_pntr >= pyrtl.Const(fl - 1):
                    self.wr_pntr.next |= pyrtl.Const(fw * 0b0, bitwidth=fw)   # wr_pntr <= #1 {fw{1'b0}}
                with pyrtl.otherwise:
                    self.wr_pntr.next |= self.wr_pntr + pyrtl.Const(0b0, bitwidth=1) # wr_pntr <= #1 wr_pntr + 1'b1

                with self.empty_o:
                    self.dat_o |= self.dat_i    # dat_o <= #1 dat_i
                with pyrtl.otherwise:
                    self.dat_o |= self.mem    # dat_o <= #1 mem[rd_pntr]

                with self.rd_pntr >= pyrtl.Const(fl - 1):
                    self.rd_pntr.next |= pyrtl.Const(fw * 0b0, bitwidth=1)   # rd_pntr <= #1 {fw{1'b0}}
                with pyrtl.otherwise:
                    self.rd_pntr.next |= self.rd_pntr + pyrtl.Const(0b1,bitwidth=1)   # rd_pntr <= #1 rd_pntr + 1'b1

            with self.wr_i.__and__(~self.full_o):  # FIFO Write
                self.mem.next |= self.dat_i    # mem[wr_pntr] <= #1 dat_i
                # self.cntr.next |= self.cntr + pyrtl.Const(0b1,bitwidth=1)    # cntr <= #1 cntr + 1'b1
                self.empty_o.next |= pyrtl.Const(0b0,bitwidth=1)     # empty_o <= #1 1'b0
                with self.cntr >= pyrtl.Const(fl - 1):
                    self.full_o.next |= pyrtl.Const(0b1, bitwidth=1) # full_o <= #1 1'b1
                    self.cntr.next |= pyrtl.Const(fl)     # cntr <= #1 fl
                with self.wr_pntr >= pyrtl.Const(fl - 1):
                    self.wr_pntr.next |= pyrtl.Const(fw * 0b0, bitwidth=fw)  # wr_pntr <= #1 {fw{1'b0}}
                with pyrtl.otherwise:
                    self.wr_pntr.next |= self.wr_pntr + pyrtl.Const(0b1,bitwidth=1)  # wr_pntr <= #1 wr_pntr + 1'b1
                    self.cntr.next |= self.cntr + pyrtl.Const(0b1,bitwidth=1)
                    
            with self.rd_i.__and__(self.empty_o) : # FIFO Read
                self.dat_o |= self.mem    # dat_o <= #1 mem[rd_pntr]
                # self.cntr.next |= self.cntr - pyrtl.Const(0b1,bitwidth=1)    # cntr <= #1 cntr - 1'b1
                self.full_o.next |= pyrtl.Const(0b0,bitwidth=1)  # full_o <= #1 1'b0
                with self.cntr <= pyrtl.Const(1):
                    self.empty_o.next |= pyrtl.Const(0b1,bitwidth=1)     # empty_o <= #1 1'b1
                    self.cntr.next |= pyrtl.Const(fw + 2 * 0b0, bitwidth=fw+2)   # cntr <= #1 {fw+2{1'b0}}
                with self.rd_pntr >= pyrtl.Const(fl - 1):
                    self.rd_pntr.next |= pyrtl.Const(fw * 0b0, bitwidth=fw)  # rd_pntr <= #1 {fw{1'b0}}
                with pyrtl.otherwise:
                    self.rd_pntr.next |= self.rd_pntr + pyrtl.Const(0b1,bitwidth=1)  # rd_pntr <= #1 rd_pntr + 1'b1
                    self.cntr.next |= self.cntr - pyrtl.Const(0b1,bitwidth=1) 

if __name__ == '__main__':
    or1200_sb_fifo = Or1200_sb_fifo()
    sim = pyrtl.GPUSim_now.GPUSim(65536)
    sim.create_dll('sb_fifo.cu')
    print(pyrtl.working_block())



